home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ddj1191.zip / DEVLOD.ZIP / DEVLOD.C < prev    next >
C/C++ Source or Header  |  1990-09-25  |  13KB  |  358 lines

  1. /********************************************************************
  2.  *     DEVLOD.C - Jim Kyle - 08/20/90                               *
  3.  *            Copyright 1990 by Jim Kyle - All Rights Reserved      *
  4.  *     (minor revisions by Andrew Schulman - 9/12/90                *
  5.  *     Dynamic loader for device drivers                            *
  6.  *            Requires Turbo C; see DEVLOD.MAK also for ASM helpers.*
  7.  ********************************************************************/
  8.      
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <dos.h>
  12.  
  13. typedef unsigned char BYTE;
  14.  
  15. #define GETFLAGS __emit__(0x9F)     /* if any error, quit right now */
  16. #define FIXDS    __emit__(0x16,0x1F)/* PUSH SS, POP DS              */
  17. #define PUSH_BP  __emit__(0x55)
  18. #define POP_BP   __emit__(0x5D)
  19.  
  20. unsigned _stklen = 0x200;
  21. unsigned _heaplen = 0;
  22.  
  23. char FileName[65];  /* filename global buffer                       */
  24. char * dvrarg;      /* points to char after name in cmdline buffer  */
  25. unsigned movsize;   /* number of bytes to be moved up for driver    */
  26. void (far * driver)();  /* used as pointer to call driver code      */
  27. void far * drvptr;  /* holds pointer to device driver               */
  28. void far * nuldrvr; /* additional driver pointers                   */
  29. void far * nxtdrvr;
  30. BYTE far * nblkdrs; /* points to block device count in List of Lists*/
  31. unsigned lastdrive; /* value of LASTDRIVE in List of Lists          */
  32. BYTE far * CDSbase; /* base of Current Dir Structure                */
  33. int CDSsize;        /* size of CDS element                          */
  34. unsigned nulseg;    /* hold parts of ListOfLists pointer            */
  35. unsigned nulofs;
  36. unsigned LoLofs;
  37.  
  38. #pragma pack(1)
  39.  
  40. struct packet{      /* device driver's command packet               */
  41.     BYTE hdrlen;
  42.     BYTE unit;
  43.     BYTE command;       /* 0 to initialize      */
  44.     unsigned status;    /* 0x8000 is error      */
  45.     BYTE reserv[8];
  46.     BYTE nunits;
  47.     unsigned brkofs;    /* break adr on return  */
  48.     unsigned brkseg;    /* break seg on return  */
  49.     unsigned inpofs;    /* SI on input          */
  50.     unsigned inpseg;    /* _psp on input        */
  51.     BYTE NextDrv;       /* next available drive */
  52.   } CmdPkt;
  53.   
  54. typedef struct {    /* Current Directory Structure (CDS)            */
  55.     BYTE path[0x43];
  56.     unsigned flags;
  57.     void far *dpb;
  58.     unsigned start_cluster;
  59.     unsigned long ffff;
  60.     unsigned slash_offset;  /* offset of '\' in current path field  */
  61.     // next for DOS4+ only
  62.     BYTE unknown;
  63.     void far *ifs;
  64.     unsigned unknown2;
  65.     } CDS;
  66.  
  67. extern unsigned _psp;       /* established by startup code in c0    */
  68. extern unsigned _heaptop;   /* established by startup code in c0    */
  69. extern BYTE _osmajor;       /* established by startup code      */
  70. extern BYTE _osminor;       /* established by startup code      */
  71.  
  72. void _exit( int );          /* established by startup code in c0    */
  73. void abort( void );         /* established by startup code in c0    */
  74.  
  75. void movup( char far *, char far *, int ); /* in MOVUP.ASM file     */
  76. void copyptr( void far *src, void far *dst ); /* in MOVUP.ASM file  */
  77.  
  78. void exit(int c)            /* called by startup code's sequence    */
  79. { _exit(c);}
  80.  
  81. int Get_Driver_Name ( void )
  82. { char *nameptr;
  83.   int i, j, cmdlinesz;
  84.  
  85.   nameptr = (char *)0x80;   /* check command line for driver name   */
  86.   cmdlinesz = (unsigned)*nameptr++;
  87.   if (cmdlinesz < 1)        /* if nothing there, return FALSE       */
  88.     return 0;
  89.   for (i=0; i<cmdlinesz && nameptr[i]<'!'; i++) /* skip blanks      */
  90.     ;
  91.   dvrarg = (char *)&nameptr[i]; /* save to put in SI                */
  92.   for ( j=0; i<cmdlinesz && nameptr[i]>' '; i++)    /* copy name    */
  93.     FileName[j++] = nameptr[i];
  94.   FileName[j] = '\0';
  95.  
  96.   return 1;                 /* and return TRUE to keep going        */
  97. }
  98.  
  99. void Put_Msg ( char *msg )  /* replaces printf()                    */
  100. #ifdef INT29
  101.     /* gratuitous use of undocumented DOS */
  102.     while (*msg)
  103.     { _AL = *msg++;             /* MOV AL,*msg  */
  104.       geninterrupt(0x29);       /* INT 29h */
  105.     }
  106. #else
  107.     _AH = 2;    /* doesn't need to be inside loop */
  108.     while (*msg)
  109.     { _DL = *msg++;            
  110.       geninterrupt(0x21);
  111.     }
  112. #endif
  113. }
  114.  
  115. void Err_Halt ( char *msg )     /* print message and abort          */
  116. { Put_Msg ( msg );
  117.   Put_Msg ( "\r\n" );           /* send CR,LF   */
  118.   abort();
  119. }
  120.  
  121. void Move_Loader ( void )       /* vacate lower part of RAM         */
  122. {
  123.     unsigned movsize, destseg;
  124.     movsize = _heaptop - _psp; /* size of loader in paragraphs      */
  125.     destseg = *(unsigned far *)MK_FP( _psp, 2 ); /* end of memory   */
  126.     movup ( MK_FP( _psp, 0 ), MK_FP( destseg - movsize, 0 ),
  127.             movsize << 4);      /* move and fix segregs             */
  128. }
  129.  
  130. void Load_Drvr ( void )         /* load driver file into RAM    */
  131. { unsigned handle;
  132.   struct {
  133.     unsigned LoadSeg;
  134.     unsigned RelocSeg;
  135.   } ExecBlock;
  136.  
  137.   ExecBlock.LoadSeg = _psp + 0x10;
  138.   ExecBlock.RelocSeg = _psp + 0x10;
  139.   _DX = (unsigned)&FileName[0];
  140.   _BX = (unsigned)&ExecBlock;
  141.   _ES = _SS;                    /* es:bx point to ExecBlock     */
  142.   _AX = 0x4B03;                 /* load overlay                 */
  143.   geninterrupt ( 0x21 );        /* DS is okay on this call      */
  144.   GETFLAGS;
  145.   if ( _AH & 1 )
  146.     Err_Halt ( "Unable to load driver file." );
  147. }
  148.  
  149. void Get_List ( void )          /* set up pointers via List     */
  150. { _AH = 0x52;                   /* find DOS List of Lists       */
  151.   geninterrupt ( 0x21 );
  152.   nulseg = _ES;                 /* DOS data segment             */
  153.   LoLofs = _BX;                 /* current drive table offset   */
  154.  
  155.   switch( _osmajor )            /* NUL adr varies with version  */
  156.     {
  157.     case  0:
  158.       Err_Halt ( "Drivers not used in DOS V1." );
  159.     case  2:
  160.       nblkdrs = NULL;
  161.       nulofs = LoLofs + 0x17;
  162.       break;
  163.     case  3:
  164.       if (_osminor == 0)
  165.       {
  166.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x10);
  167.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x1b));
  168.           nulofs = LoLofs + 0x28;
  169.       }
  170.       else
  171.       {
  172.           nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  173.           lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  174.           nulofs = LoLofs + 0x22;
  175.       }
  176.       CDSbase = *(BYTE far * far *)MK_FP(nulseg, LoLofs + 0x16);
  177.       CDSsize = 81;
  178.       break;
  179.     case  4:
  180.     case  5:
  181.       nblkdrs = (BYTE far *) MK_FP(nulseg, LoLofs + 0x20);
  182.       lastdrive = *((BYTE far *) MK_FP(nulseg, LoLofs + 0x21));
  183.       nulofs  = LoLofs + 0x22;
  184.       CDSbase = *(BYTE far * far *) MK_FP(nulseg, LoLofs + 0x16);
  185.       CDSsize = 88;
  186.       break;
  187.     case 10:
  188.     case 20:
  189.       Err_Halt ( "OS2 DOS Box not supported." );
  190.     default:
  191.       Err_Halt ( "Unknown version of DOS!");
  192.     }
  193. }
  194.  
  195. void Fix_DOS_Chain ( void )     /* patches driver into DOS chn  */
  196. { unsigned i;
  197.  
  198.   nuldrvr = MK_FP( nulseg, nulofs+0x0A ); /* verify the drvr    */
  199.   drvptr = "NUL     ";
  200.   for ( i=0; i<8; ++i )
  201.     if ( *((BYTE far *)nuldrvr+i) != *((BYTE far *)drvptr+i) )
  202.       Err_Halt ( "Failed to find NUL driver." );
  203.  
  204.   nuldrvr = MK_FP( nulseg, nulofs );    /* point to NUL driver  */
  205.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  206.  
  207.   copyptr ( nuldrvr, &nxtdrvr );        /* hold old head now    */
  208.   copyptr ( &drvptr, nuldrvr );         /* put new after NUL    */
  209.   copyptr ( &nxtdrvr, drvptr );         /* and old after new    */
  210. }
  211.  
  212. // returns number of next free drive, -1 if none available
  213. int Next_Drive ( void )
  214. {
  215. #ifdef USE_BLKDEV
  216.   return (nblkdrs && (*nblkdrs < lastdrive)) ? *nblkdrs : -1;
  217. #else
  218.   /* The following approach takes account of SUBSTed and
  219.      network-redirector drives */
  220.   CDS far *cds;
  221.   int i;
  222.   /* find first unused entry in CDS structure */
  223.   for (i=0, cds=CDSbase; i<lastdrive; i++, ((BYTE far *)cds)+=CDSsize)
  224.     if (! cds->flags)                /* found a free drive  */
  225.         break;
  226.   return (i == lastdrive) ? -1 : i; 
  227. #endif  
  228. }
  229.  
  230. int Init_Drvr ( void )
  231. { unsigned tmp;
  232. #define INIT 0
  233.   CmdPkt.command = INIT;        /* build command packet         */
  234.   CmdPkt.hdrlen = sizeof (struct packet);
  235.   CmdPkt.unit = 0;
  236.   CmdPkt.inpofs  = (unsigned)dvrarg;    /* points into cmd line */
  237.   CmdPkt.inpseg  = _psp;
  238.   /* can't really check for next drive here, because don't yet know
  239.      if this is a block driver or not */
  240.   CmdPkt.NextDrv = Next_Drive();
  241.   drvptr  = MK_FP( _psp+0x10, 0 );      /* new driver's address */
  242.  
  243.   tmp = *((unsigned far *)drvptr+3);    /* STRATEGY pointer     */
  244.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  245.   _ES = FP_SEG( (void far *)&CmdPkt );
  246.   _BX = FP_OFF( (void far *)&CmdPkt );
  247.   (*driver)();                  /* set up the packet address    */
  248.  
  249.   tmp = *((unsigned far *)drvptr+4);    /* COMMAND pointer      */
  250.   driver = MK_FP( FP_SEG( drvptr ), tmp );
  251.   (*driver)();                  /* do the initialization        */
  252.   
  253.   /* check status code in command packet                        */
  254.   return (! ( CmdPkt.status & 0x8000 ));
  255. }
  256.  
  257. int  Put_Blk_Dev ( void )   /* TRUE if Block Device failed      */
  258. { int newdrv;
  259.   int retval = 1;           /* pre-set for failure              */
  260.   int unit = 0;
  261.   BYTE far *DPBlink;
  262.   CDS far *cds;
  263.   int i;
  264.  
  265.   if ((Next_Drive() == -1) || CmdPkt.nunits == 0)
  266.     return retval;          /* cannot install block driver      */
  267.   if (CmdPkt.brkofs != 0)   /* align to next paragraph          */
  268.   {
  269.     CmdPkt.brkseg += (CmdPkt.brkofs >> 4) + 1;
  270.     CmdPkt.brkofs = 0;
  271.   }
  272.   while( CmdPkt.nunits-- )
  273.   {
  274.     if ((newdrv = Next_Drive()) == -1)
  275.         return 1;
  276.     (*nblkdrs)++;
  277.     _AH = 0x32;             /* get last DPB and set poiner      */
  278.     _DL = newdrv;
  279.     geninterrupt ( 0x21 );
  280.     _AX = _DS;              /* save segment to make the pointer */
  281.     FIXDS;
  282.     DPBlink = MK_FP(_AX, _BX);
  283.     (unsigned) DPBlink += (_osmajor < 4 ? 24 : 25 );
  284.     _SI = *(unsigned far *)MK_FP(CmdPkt.inpseg, CmdPkt.inpofs);
  285.     _ES = CmdPkt.brkseg;
  286.     _DS = CmdPkt.inpseg;
  287.     _AH = 0x53;
  288.     PUSH_BP;
  289.     _BP = 0;
  290.     geninterrupt ( 0x21 );    /* build the DPB for this unit    */
  291.     POP_BP;
  292.     FIXDS;
  293.     *(void far * far *)DPBlink = MK_FP( CmdPkt.brkseg, 0 );
  294.  
  295.     /* set up the Current Directory Structure for this drive */
  296.     cds = (CDS far *) (CDSbase + (newdrv * CDSsize));
  297.     cds->flags = 1 << 14;       /* PHYSICAL DRIVE */
  298.     cds->dpb = MK_FP(CmdPkt.brkseg, 0);
  299.     cds->start_cluster = 0xFFFF;
  300.     cds->ffff = -1L;
  301.     cds->slash_offset = 2;
  302.     if (_osmajor > 3)
  303.       { cds->unknown = 0;
  304.         cds->ifs = (void far *) 0;
  305.         cds->unknown2 = 0;
  306.       }
  307.       
  308.     /* set up pointers for DPB, driver  */
  309.     DPBlink = MK_FP( CmdPkt.brkseg, 0);
  310.     *DPBlink = newdrv;
  311.     *(DPBlink+1) = unit++;
  312.     if (_osmajor > 3)
  313.       DPBlink++;          /* add one if DOS 4                 */
  314.     *(long far *)(DPBlink+0x12) = (long)MK_FP( _psp+0x10, 0 );
  315.     *(long far *)(DPBlink+0x18) = 0xFFFFFFFF;
  316.     CmdPkt.brkseg += 2;       /* Leave two paragraphs for DPB   */
  317.     CmdPkt.inpofs += 2;       /* Point to next BPB pointer      */
  318.   }     /* end of nunits loop   */
  319.   return 0;                 /* all went okay                    */
  320. }
  321.  
  322. void Get_Out ( void )
  323. { unsigned temp;
  324.  
  325.   temp = *((unsigned far *)drvptr+2);   /* attribute word       */
  326.   if ((temp & 0x8000) == 0 )    /* if block device, set up tbls */
  327.     if (Put_Blk_Dev() )
  328.       Err_Halt( "Could not install block device" );
  329.  
  330.   Fix_DOS_Chain ();             /* else patch it into DOS       */
  331.  
  332.   _ES = *((unsigned *)MK_FP( _psp, 0x002C ));
  333.   _AH = 0x49;                   /* release environment space    */
  334.   geninterrupt ( 0x21 );
  335.  
  336.   /* then set up regs for KEEP function, and go resident        */
  337.   temp = (CmdPkt.brkofs + 15);  /* normalize the offset         */
  338.   temp >>= 4;
  339.   temp += CmdPkt.brkseg;        /* add the segment address      */
  340.   temp -= _psp;                 /* convert to paragraph count   */
  341.   _AX = 0x3100;                 /* KEEP function of DOS         */
  342.   _DX = (unsigned)temp;         /* paragraphs to retain         */
  343.   geninterrupt ( 0x21 );        /* won't come back from here!   */
  344. }
  345.  
  346. void main ( void )
  347. { if (!Get_Driver_Name() )
  348.     Err_Halt ( "Device driver name required.");
  349.   Move_Loader ();               /* move code high and jump      */
  350.   Load_Drvr ();                 /* bring driver into freed RAM  */
  351.   Get_List();                   /* get DOS internal variables   */
  352.   if (Init_Drvr ())             /* let driver do its thing      */
  353.       Get_Out();                /* check init status, go TSR    */
  354.   else
  355.       Err_Halt ( "Driver initialization failed." );
  356. }
  357.